import { defineComponent, ref, Transition, PropType, computed, VNodeChild } from 'vue'
import { Arrayable } from '@vueuse/core'
import moment, { Moment } from 'moment'
import { castArray } from '../_util/_'

import CalendarHeader from './CalendarHeader'
import Grid from './Grid'
import { DateGrid, MonthGrid, YearGrid, dateProps, monthProps, yearProps } from './CalendarGrid'

// ================================ DateCalendar ================================

const dateCls = 'x-calendar'
const weekdays = ['日', '一', '二', '三', '四', '五', '六']

export const DateCalendar = defineComponent({
  name: dateCls,
  props: {
    value: [Object, Array] as PropType<Arrayable<Moment>>,
    range: Array as PropType<Moment[]>,
    disabledDate: Function as PropType<(date: Moment) => boolean>,
    onSelect: Function as PropType<(date: Moment) => void>,
    onHover: Function as PropType<(date: Moment) => void>
  },
  emits: ['update:value'],
  setup(props, { slots, emit }) {
    const val1 = castArray(props.value)[0] ?? props.range?.[0]
    const date1 = (val1?.clone() ?? moment()).date(1)
    const start = date1.add(moment.localeData().firstDayOfWeek() - date1.day(), 'days')
    const display = ref(date1.clone())

    const gridRef = ref<typeof Grid>()
    const visible = ref(false)

    function showPanel() {
      visible.value = true
    }
    function onSelectGroup(m: Moment) {
      visible.value = false
      gridRef.value.scroll(m, 0)
    }
    function onSelect(date: Moment) {
      emit('update:value', date)
      props.onSelect?.(date)
    }

    return () => (
      <div class={dateCls}>
        <CalendarHeader prefixCls={dateCls} title={display.value.format('YYYY年M月')} onPrev={gridRef.value?.prev} onNext={gridRef.value?.next} onPanelChange={showPanel} />

        <div class={`${dateCls}_grid ${dateCls}_week`}>
          {weekdays.map(day => (
            <div title={day} class={`${dateCls}_weekday`}>
              {day}
            </div>
          ))}
        </div>

        <DateGrid
          //
          _ref={gridRef}
          prefixCls={dateCls}
          start={start}
          modelValue={props.value}
          rangeValue={props.range}
          disabled={props.disabledDate}
          cell={date => (slots['date-cell'] || slots.dateCell)?.({ date }) ?? dateProps.cell(date)}
          onSelect={onSelect}
          onHover={props.onHover}
          onGroupChange={date => (display.value = date)}
        />

        <Transition name='calendar-ain'>
          {visible.value ? (
            <MonthCalendar
              //
              value={props.value}
              display={display.value}
              style='position: absolute; top: 0; left: 0; width: 100%; height: 100%'
              cell={date => (slots['month-cell'] || slots.monthCell)?.({ date }) ?? monthProps.cell(date)}
              yearCell={date => (slots['year-cell'] || slots.yearCell)?.({ date }) ?? yearProps.cell(date)}
              onSelect={onSelectGroup}
            />
          ) : null}
        </Transition>
      </div>
    )
  }
})

// ================================ MonthCalendar ================================

const monthCls = 'x-month-calendar'

export const MonthCalendar = defineComponent({
  name: monthCls,
  props: {
    value: [Object, Array] as PropType<Arrayable<Moment>>,
    range: Array as PropType<Moment[]>,
    display: Object as PropType<Moment>,
    disabledDate: Function as PropType<(date: Moment) => boolean>,
    cell: Function as PropType<(date: Moment) => VNodeChild>,
    yearCell: Function as PropType<(date: Moment) => VNodeChild>,
    onSelect: Function as PropType<(date: Moment) => void>,
    onHover: Function as PropType<(date: Moment) => void>
  },
  setup(props) {
    const val1 = castArray(props.value)[0] ?? props.range?.[0]
    const start = (props.display?.clone() ?? val1?.clone() ?? moment()).month(0)
    const display = ref(start.clone())
    const gridRef = ref<typeof Grid>()
    const visible = ref(false)

    function showPanel() {
      visible.value = true
    }
    function onSelectGroup(m: Moment) {
      visible.value = false
      gridRef.value.scroll(m, 0)
    }

    return () => (
      <div class={monthCls}>
        <CalendarHeader prefixCls={monthCls} title={display.value.format('YYYY年')} onPrev={gridRef.value?.prev} onNext={gridRef.value?.next} onPanelChange={showPanel} />

        {/* prettier-ignore */}
        <MonthGrid
          _ref={gridRef}
          prefixCls={monthCls}
          start={start}
          modelValue={props.value}
          rangeValue={props.range}
          disabled={props.disabledDate}
          cell={props.cell ?? monthProps.cell}
          onSelect={props.onSelect}
          onHover={props.onHover}
          onGroupChange={date => (display.value = date)}
        />

        <Transition name='calendar-ain'>
          {visible.value ? (
            <YearCalendar
              //
              value={props.value}
              display={display.value}
              style='position: absolute; top: 0; left: 0; width: 100%; height: 100%'
              cell={props.yearCell ?? yearProps.cell}
              onSelect={onSelectGroup}
            />
          ) : null}
        </Transition>
      </div>
    )
  }
})

// ================================ YearCalendar ================================

export const YearCalendar = defineComponent({
  name: 'x-year-calendar',
  props: {
    value: [Object, Array] as PropType<Arrayable<Moment>>,
    range: Array as PropType<Moment[]>,
    display: Object as PropType<Moment>,
    disabledDate: Function as PropType<(date: Moment) => boolean>,
    cell: Function as PropType<(date: Moment) => VNodeChild>,
    onSelect: Function as PropType<(date: Moment) => void>,
    onHover: Function as PropType<(date: Moment) => void>
  },
  setup(props) {
    const val1 = castArray(props.value)[0] ?? props.range?.[0]
    const start = group(props.display?.clone() ?? val1?.clone() ?? moment())
    const display = ref(start.clone())
    const title = computed(() => group(display.value).year() + ' - ' + group(display.value, 9).year())
    const gridRef = ref<typeof Grid>()

    function group(date: Moment, amount = 0) {
      return date.clone().add((-date.year() % 10) + amount, 'years')
    }

    return () => (
      <div class={monthCls}>
        <CalendarHeader prefixCls={monthCls} title={title.value} onPrev={gridRef.value?.prev} onNext={gridRef.value?.next} />

        <YearGrid
          //
          _ref={gridRef}
          prefixCls={monthCls}
          start={start}
          modelValue={props.value}
          rangeValue={props.range}
          disabled={props.disabledDate}
          cell={props.cell ?? yearProps.cell}
          onSelect={props.onSelect}
          onHover={props.onHover}
          onGroupChange={date => (display.value = date)}
        />
      </div>
    )
  }
})
